perm filename FACADE.SAI[REV,MUS] blob sn#445187 filedate 1979-05-24 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00045 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00005 00002	BEGIN "FACADE"
C00006 00003	DEFINE DEFAULT_SAVE_FILE=⊂"SAVED.REV"⊃
C00008 00004	DEFINE HELP_TEXT=⊂"
C00009 00005	∂ Declarations for unit reverberator manipulation routines.
C00011 00006	∂ Declarations for tree manipulation routines.
C00014 00007	∂ Declarations for impulse response display routine.
C00016 00008	∂ Declarations for JAM's display routines.
C00018 00009	∂ Declarations for piece of glass manipulation routines.
C00019 00010	∂ Declarations for scanning routines.
C00022 00011	EXTERNAL PROCEDURE instrument_print(
C00025 00012	INTEGER PROCEDURE file_channel(
C00028 00013	PROCEDURE save_the_tree(
C00035 00014	PROCEDURE save_the_world(
C00038 00015	PROCEDURE HELP_clear
C00039 00016	PROCEDURE DCLEAR(
C00040 00017	PROCEDURE esc_break(
C00043 00018	PROCEDURE label_print(
C00046 00019	PROCEDURE unit_print(
C00049 00020	PROCEDURE units_print(
C00051 00021	PROCEDURE tree_print(
C00054 00022	PROCEDURE update_display(
C00057 00023	   ∂  ... update_display ... continued.
C00059 00024	∂ Declarations for THE_WORLD.
C00061 00025	PROCEDURE initialize_the_world
C00063 00026	∂ Top level.
C00065 00027	WHILE
C00067 00028	   CASE
C00069 00029	      ["A"]   ∂ APPEND
C00072 00030	      ["D"]   ∂ DELAY_TIME
C00076 00031	      ["F"]   ∂ FREQUENCY
C00079 00032	      ["H"]   ∂ HELP
C00081 00033	      ["I"]   ∂ IMPULSE
C00085 00034	      ["K"]   ∂ KILL
C00087 00035	      ["L"]   ∂ LAST_IMPULSE
C00090 00036	      ["O"]   ∂ OUTPUT
C00093 00037	      ["S"]   ∂ SAVE_THE_WORLD
C00096 00038	      ["W"]   ∂ WALL_DISTANCE
C00099 00039	      [""""]  ∂ COPY_NODE_INTO_UNIT
C00101 00040	      ["@"]   ∂ READ_COMMANDS_FROM_FILE
C00104 00041	      ["?"]   ∂ HELP prefix
C00106 00042	      ["←"]   ∂ MOVE_TO_INPUT
C00109 00043	      [0]     ∂ NULL COMMAND
C00110 00044	   END "command loop"
C00111 00045	END   "FACADE"
C00112 ENDMK
C⊗;
BEGIN "FACADE"
REQUIRE "HEADER.SAI" SOURCE_FILE;

∂ Ken Shoemake.  December 1976.
This module is the main control program for REVED.  It handles the displays
and decides what to do with the commands.  Naturally it gets a lot of help
from its friends.  For a user's introduction to REVED, run the program and
ask for help -- it tries to be very helpful, but not too verbose.
;
DEFINE DEFAULT_SAVE_FILE=⊂"SAVED.REV"⊃;

DEFINE DEFAULT_CLOCK_RATE=25600;∂ Samples per second.;
DEFINE DEFAULT_GAIN=⊂0.75⊃;
DEFINE DEFAULT_DELAY=⊂0.05⊃;	∂ Seconds.;

DEFINE DEFAULT_NEAREST_PRIME_FLAG=TRUE;

DEFINE DEFAULT_PRIMES_OFFSET=1;
DEFINE DEFAULT_DURATION_FACTOR=⊂0.50⊃;
DEFINE DEFAULT_DELAY_FACTOR=⊂0.8⊃;
DEFINE DEFAULT_GAIN_FACTOR=⊂0.96⊃;

DEFINE MAIN_PIECE=0,
      UNIT_LABEL_PIECE=1,
      UNIT_PIECE=2,
      TREE_UNIT_PIECE=3,
      TREE_PIECE=4,
      HELP_PIECE=5,
      IMPULSE_PIECE=6,
      CURSOR_PIECE=7;
DEFINE MAIN_LINES=7,
      UNIT_LABEL_LINES=1,
      UNIT_LINES=1,
      TREE_UNIT_LINES=1,
      TREE_LINES=13,
      HELP_LINES=11;
DEFINE PER_LINE=24;
DEFINE HELP_POS=400;
DEFINE TOP_POS=96;
DEFINE UNIT_LABEL_POS=TOP_POS,
      UNIT_POS=UNIT_LABEL_POS-((UNIT_LABEL_LINES)*PER_LINE),
      TREE_UNIT_POS=UNIT_POS-((UNIT_LINES)*PER_LINE),
      TREE_POS=TREE_UNIT_POS-((TREE_UNIT_LINES+1)*PER_LINE),
      MAIN_POS=TREE_POS-((TREE_LINES+1)*PER_LINE);

DEFINE HELP_TEXT=⊂"
APPEND          KILL            ZERO_WORLD      ""."" (Refresh display)
BRANCH          LAST_IMPULSE    #SAMPLES_DELAY
CLOCK_RATE      NEAREST_PRIMES  ← (No [CR]. Move to INPUT)
DELAY_TIME      OUTPUT          → (No [CR]. Move to OUTPUT)
EXIT            PRECEDE         ↔ (No [CR]. Move to BRANCH)
FREQUENCY       REPLACE         "" (Copy unit from node)
GAIN            SAVE_THE_WORLD  @ (Read commands from file)
HELP            TIME_TO_DECAY   ; (End of command file)
IMPULSE         WALL_DISTANCE   ? HELP prefix
"⊃;

∂ Declarations for unit reverberator manipulation routines.;
∂ These routines currently found in UNIT.SAI[REV,KS].;

EXTERNAL RECORD_CLASS REV_UNIT(
   INTEGER NUMBER_OF_SAMPLES, CLOCK_RATE;
   REAL GAIN, DELAY_TIME, DECAY_TIME);

EXTERNAL REAL PROCEDURE get_spec(
      RECORD_POINTER(REV_UNIT) unit;
      STRING which_spec);

EXTERNAL REAL PROCEDURE set_spec(
      RECORD_POINTER(REV_UNIT) unit;
      STRING new_spec, fix_spec;
      REAL spec_val;
      BOOLEAN use_prime(TRUE));

EXTERNAL RECORD_POINTER(REV_UNIT) PROCEDURE new_unit(
      INTEGER rate(0);
      REAL gain(0), delay(0);
      BOOLEAN use_prime(TRUE));

EXTERNAL PROCEDURE free_unit(
      REFERENCE RECORD_POINTER(REV_UNIT) unit);

EXTERNAL PROCEDURE copy_unit(
      REFERENCE RECORD_POINTER(REV_UNIT) new_copy;
      RECORD_POINTER(REV_UNIT) unit);

EXTERNAL INTEGER PROCEDURE nearest_prime(
      REFERENCE INTEGER n);

EXTERNAL INTEGER ARRAY PRIMES[1:2]; ∂ Bounds are ignored.;

∂ Declarations for tree manipulation routines.;
∂ These routine currently found in TREE.SAI[REV,KS].;

EXTERNAL RECORD_CLASS REV_TREE(
   RECORD_POINTER(REV_TREE) IN, OUT, ABURO;
   RECORD_POINTER(REV_UNIT) UNIT);

EXTERNAL PROCEDURE insert_node(
      REFERENCE RECORD_POINTER(REV_TREE) node;
      RECORD_POINTER(REV_UNIT) unit;
      STRING direction("OUTPUT");
      BOOLEAN replace(FALSE));

EXTERNAL PROCEDURE first_node(
      REFERENCE RECORD_POINTER(REV_TREE) node);

EXTERNAL PROCEDURE input_node(
      REFERENCE RECORD_POINTER(REV_TREE) node);

EXTERNAL BOOLEAN PROCEDURE move_tree(
      REFERENCE RECORD_POINTER(REV_TREE) node;
      REFERENCE RECORD_POINTER(REV_UNIT) unit;
      STRING direction("OUTPUT"));

EXTERNAL PROCEDURE free_node(
      REFERENCE RECORD_POINTER(REV_TREE) node);

EXTERNAL PROCEDURE delete_node(
      REFERENCE RECORD_POINTER(REV_TREE) node);

EXTERNAL BOOLEAN PROCEDURE traverse_tree(
      REFERENCE RECORD_POINTER(REV_TREE) node;
      REFERENCE RECORD_POINTER(REV_UNIT) unit;
      REFERENCE INTEGER level;
      REFERENCE BOOLEAN branching);

EXTERNAL BOOLEAN PROCEDURE is_root(
      RECORD_POINTER(REV_TREE) node);

EXTERNAL RECORD_POINTER(REV_TREE) PROCEDURE new_tree;

EXTERNAL PROCEDURE free_tree(
      REFERENCE RECORD_POINTER(REV_TREE) node);

∂ Declarations for impulse response display routine.;
∂ These routines currently found in IMPULS.SAI[REV,KS].;

EXTERNAL RECORD_CLASS REV_STATE_LIST(
   RECORD_POINTER(REV_STATE_LIST) NEXT_UNIT;
   REAL ARRAY DELAY_MEM; INTEGER MEM_SIZE, MEM_POSITION; REAL GAIN);

EXTERNAL RECORD_CLASS CASCADE(
   INTEGER CLOCK_RATE;
   RECORD_POINTER(REV_STATE_LIST) FIRST_UNIT);

EXTERNAL RECORD_POINTER(CASCADE) PROCEDURE new_cascade(
      RECORD_POINTER(REV_TREE) node;
      INTEGER path_length(INFINITY));

EXTERNAL PROCEDURE free_cascade(
      REFERENCE RECORD_POINTER(CASCADE) chain);

EXTERNAL PROCEDURE IMPULS(
      REFERENCE INTEGER id; 
      RECORD_POINTER(CASCADE) rev_chain;
      REAL duration);

∂ Declarations for JAM's display routines.;
∂ These routines currently found in JAMLIB.REL[SUB,SYS].;

EXTERNAL PROCEDURE DSETUP(
      INTEGER nwds;
      REFERENCE INTEGER id);
DEFINE DGET(id,nwds)=⊂DSETUP(nwds,id)⊃;

EXTERNAL BOOLEAN PROCEDURE DRELS(
      REFERENCE INTEGER id);

EXTERNAL BOOLEAN PROCEDURE DWRITE(
      INTEGER id, chan);

EXTERNAL PROCEDURE WRITE(
      INTEGER id, pog);

EXTERNAL PROCEDURE BUFCLR(
      INTEGER id, nwds);

EXTERNAL PROCEDURE AVECT(
      INTEGER id, X, Y);

EXTERNAL PROCEDURE AIVECT(
      INTEGER id, X, Y);

EXTERNAL PROCEDURE RVECT(
      INTEGER id, dX, dY);

EXTERNAL PROCEDURE RIVECT(
      INTEGER id, dX, dY);

EXTERNAL PROCEDURE TYPLOC(
      INTEGER ymin,ymax);

EXTERNAL PROCEDURE DTEXT(
      INTEGER id;
      STRING text;
      REAL scale(0), angle(0));

∂ Declarations for piece of glass manipulation routines.;
∂ These routines currently found in PAPER.SAI, CURSOR.FAI, and DISPLA.FAI
	respectively, all on [REV,KS].;

EXTERNAL PROCEDURE PPSIZE(
      INTEGER #glitches, #lines(1));

EXTERNAL PROCEDURE PPSELECT(
      INTEGER pp;
      BOOLEAN keep_map(FALSE));

EXTERNAL PROCEDURE cursor(
      INTEGER position, char("→"), pog(CURSOR_PIECE));

EXTERNAL PROCEDURE display(
      INTEGER position;
      STRING text;
      INTEGER pog);

∂ Declarations for scanning routines.;
∂ These routines currently found in FETCH.SAI[REV,KS].;

EXTERNAL INTEGER DEVICEBREAKS,
   TOKENBREAKS, DELIMITERBREAKS,
   PERIODBREAKS, COMMABREAKS,
   LINEBREAKS, CMMANDBREAKS; ∂ Funny name avoids LOADER conflict.;

EXTERNAL BOOLEAN PROCEDURE yes_fetch(
      REFERENCE STRING arg;
      REFERENCE BOOLEAN flag);

EXTERNAL BOOLEAN PROCEDURE file_fetch(
      REFERENCE STRING arg;
      REFERENCE STRING device, file);

EXTERNAL BOOLEAN PROCEDURE fix_fetch(
      REFERENCE STRING arg;
      REFERENCE STRING fix);

EXTERNAL BOOLEAN PROCEDURE real_fetch(
      REFERENCE STRING arg;
      REFERENCE REAL value, factor);

EXTERNAL BOOLEAN PROCEDURE #samp_fetch(
      REFERENCE STRING arg;
      REFERENCE INTEGER value, offset);

EXTERNAL PROCEDURE command_read(
      REFERENCE STRING the_command, the_arguments;
      REFERENCE BOOLEAN the_file_flag;
      INTEGER the_in_channel;
      REFERENCE INTEGER the_in_eof, the_in_break;
      STRING immediate_chars("←→↔"));

∂ Sneaky TTY input routines.;
EXTERNAL INTEGER PROCEDURE SNEAKW;

EXTERNAL BOOLEAN PROCEDURE INSKIP(
      INTEGER mode);

DEFINE INSKIPL=⊂INSKIP(1)⊃,
      INSKIPC=⊂INSKIP(0)⊃;

EXTERNAL PROCEDURE instrument_print(
      INTEGER channel;
      RECORD_POINTER(REV_TREE) node);

∂ This routine currently found in INSTRU.SAI[REV,KS].;
INTEGER PROCEDURE file_channel(
      STRING device, file;
      STRING direction;
      INTEGER mode;
      REFERENCE INTEGER eof, break);
∂ Given a device and file name, plus desired mode and direction,
returns a channel doing the desired thing.  Direction can be "in",
"out", or "both".
;
   BEGIN "file channel"
   INTEGER channel;
   OWN INTEGER count;
   BOOLEAN in, out, inflag, outflag;

   IF
      EQU(direction,"in")
    THEN
      out ← ¬(in ← TRUE)
   ELSE IF
      EQU(direction,"out")
    THEN
      in ← ¬(out ← TRUE)
   ELSE IF
      EQU(direction,"both")
    THEN
      in ← out ← TRUE
   ELSE
      RETURN(-1);

   channel ← GETCHAN;
   IF
      channel < 0
    THEN
      RETURN(channel);

   OPEN(channel,device,
         mode LOR '770000,
         (IF in THEN 2 ELSE 0),
         (IF out THEN 2 ELSE 0),
         count ← 80,
         break,
         eof ← -1);
   IF
      eof
    THEN BEGIN
      RELEASE(channel);
      RETURN(-1);
      END;

   IF
      in
    THEN
      LOOKUP(channel,file,inflag)
    ELSE
      inflag ← TRUE;
   IF
      out
    THEN
      ENTER(channel,file,outflag)
    ELSE
      outflag ← TRUE;

   IF
      inflag ∧ outflag
    THEN BEGIN
      RELEASE(channel);
      RETURN(-1);
      END;

   RETURN(channel);
   END   "file channel";
PROCEDURE save_the_tree(
      INTEGER channel;
      RECORD_POINTER(REV_TREE) node);
∂ Saves the tree as a list of commands.
;
   BEGIN "save the tree"
   RECORD_POINTER(REV_TREE) tree;
   RECORD_POINTER(REV_UNIT) unit;
   INTEGER level,
	 i,
	 wid, dig;
   BOOLEAN branching,
	 know_path;
   STRING path;

   GETFORMAT(wid,dig);
   SETFORMAT(0,4);

   tree ← node;
   first_node(tree);
   IF
      ¬move_tree(tree,unit,"OUTPUT")
    THEN
      RETURN;
   CPRINT(channel,
	 ↓,
	 "CLOCK_RATE ",CVS(get_spec(unit,"rate")),↓,
	       ∂ Sets the clock rate;
	 "NEAREST_PRIMES NO",↓
	       ∂ Prevents nearest primes so #samples are exact;
	 );
   move_tree(tree,unit,"INPUT");

   path ← NULL; know_path ← FALSE;
   WHILE
      traverse_tree(tree,unit,level ← 0,branching)
    DO BEGIN
      CPRINT(channel,
	    "#SAMPLES ",CVS(get_spec(unit,"#samples"))," GAIN",↓,
		  ∂ Sets the #samples;
	    "GAIN ",CVF(get_spec(unit,"gain"))," DELAY",↓
		  ∂ Sets the gain without changing #samples;
	    );
      IF
	 level = 1
       THEN
	 CPRINT(channel,
	       "APPEND",↓
		     ∂ Inserts unit at OUTPUT;
	       )
      ELSE IF
	 level = 0
       THEN
	 CPRINT(channel,
	       "BRANCH",↓
		     ∂ Inserts unit at BRANCH;
	       )
      ELSE BEGIN
	 FOR
	    i ← level STEP 1
	  UNTIL
	    -1
	  DO
	    CPRINT(channel,
		  "←",↓
			∂ Backs up thru INPUTs;
		  );
	 CPRINT(channel,
	       "BRANCH",↓
		     ∂ Inserts unit at BRANCH;
	       );
	 END;
      IF
	 ¬know_path
       THEN
	 IF
	    level = 1
	  THEN
	    path ← path&"→"
		  ∂ Moves to node at OUTPUT;
	 ELSE IF
	    level = 0
	  THEN
	    path ← path&"↔"
		  ∂ Moves to node at BRANCH;
	 ELSE
	    path ← path[1 TO ∞+level]&"↔";
		  ∂ Forgets wrong path and moves to node at BRANCH;
      IF
	 tree = node
       THEN
	 know_path ← TRUE;
      END;

   CPRINT(channel,
	 ↓
	 );
   FOR
      i ← level STEP 1
    UNTIL
      -1
    DO
      CPRINT(channel,
            "←",↓
		    ∂ Backs up thru INPUTs to root;
	    );
   FOR
      i ← 1
    THRU
      LENGTH(path)
    DO
      CPRINT(channel,
	    path[i FOR 1],↓
		    ∂ Follows path thru tree to node;
	    );

   SETFORMAT(wid,dig);
   END   "save the tree";
PROCEDURE save_the_world(
      INTEGER channel;
      RECORD_POINTER(REV_TREE) the_tree;
      RECORD_POINTER(REV_UNIT) the_unit;
      BOOLEAN the_nearest_prime_flag;
      INTEGER the_primes_offset;
      REAL the_duration_factor,
            the_delay_factor,
            the_gain_factor);
∂ Saves everything (but the impulse response) as commands in a file.
;
   BEGIN "save the world"
   INTEGER wid, dig;

   GETFORMAT(wid,dig);
   SETFORMAT(0,4);
   CPRINT(channel,
         "ZERO_WORLD",↓,
               ∂ Clears everything;
         "IMPULSE *",CVF(the_duration_factor)[2 TO ∞],↓,
               ∂ Sets the duration factor;
         "DELAY *",CVF(the_delay_factor)[2 TO ∞]," GAIN",↓,
               ∂ Sets the delay factor;
         "GAIN *",CVF(the_gain_factor)[2 TO ∞]," DELAY",↓,
               ∂ Sets the gain factor;
         "#SAMPLES ",(IF the_primes_offset ≥ 0 THEN "+" ELSE NULL),
            the_primes_offset," GAIN",↓
               ∂ Sets the primes offset;
         );

   save_the_tree(channel,the_tree);
   CPRINT(channel,
         "CLOCK_RATE ",CVS(get_spec(the_unit,"rate")),↓,
               ∂ Sets the clock rate;
         "#SAMPLES ",CVS(get_spec(the_unit,"#samples"))," GAIN",↓,
               ∂ Sets the #samples;
         "GAIN ",CVF(get_spec(the_unit,"gain"))," DELAY",↓
               ∂ Sets the gain without changing #samples;
         );
   CPRINT(channel,
         ↓,
         "NEAREST_PRIMES ",(IF the_nearest_prime_flag THEN "YES" ELSE "NO"),↓,
               ∂ Sets the nearest prime flag;
         ";",↓
               ∂ Terminates the file input;
         );
   SETFORMAT(wid,dig);
   END   "save the world";
PROCEDURE HELP_clear;
∂ Clears the piece of glass that the list of commands is displayed on.
;
   START_CODE "HELP CLEAR"
   DEFINE PGACT='715040000000;
   HRRI   1,'400000;
   LSH    1,-HELP_PIECE;
   ANDCAI 1,-1;
   PGACT  0(1);
   END        "HELP CLEAR";

PROCEDURE DCLEAR(
      INTEGER pog);
∂ Clears the display using JAM's routines.  Used to clear impulse display.
;
   BEGIN "DCLEAR"
   INTEGER id;
   DGET(id,3);
   WRITE(id,pog);
   END   "DCLEAR";

PROCEDURE esc_break(
      INTEGER char;
      BOOLEAN break(FALSE));
∂ Executes the terminal ESC or BREAK function specified.  If break is TRUE, then
will do [BREAK]char, else [ESC]char.
;
   START_CODE
   MOVSI 2,'4000; ∂ ESC/BREAK function;
   HRR   2,char;
   SKIPE break;
   TRO   2,'400; ∂ Set this bit for BREAK function;
   HRROI 1,2; ∂ Indicates list of commands 1 long - just [ESC]/[BREAK] char;
   CALLI 1,'400121; ∂ TTYSET UUO;
   END;
PROCEDURE label_print(
      INTEGER position, pog;
      REAL delay_factor, gain_factor, duration_factor;
      INTEGER primes_offset;
      BOOLEAN nearest_prime_flag);
   BEGIN "label print"
   INTEGER wid, dig;
   STRING text;

   ∂ Produce a line that looks like this:
" Delay(*.80)  Gain(*.96)  Time[I*.6]  #Samples(+5)Y    Wall     Frequency   Clock";
   SETFORMAT(4,2);
   text ← " Delay(*"&CVF(delay_factor)[2 TO ∞]&")";
   text ← text&"  Gain(*"&CVF(gain_factor)[2 TO ∞]&")";
   SETFORMAT(3,1);
   text ← text&"  Time[I*"&CVF(duration_factor)[2 TO ∞]&"]";
   SETFORMAT(0,0);
   text ← text&"  #Samples(+"&CVS(primes_offset)&")"&
	 (IF nearest_prime_flag THEN "Y" ELSE "N");
   text ← text&"    Wall     Frequency   Clock";

   display(position,text,pog);
   END   "label print";
PROCEDURE unit_print(
      INTEGER position, pog;
      RECORD_POINTER(REV_UNIT) unit);
   BEGIN "unit print"
   INTEGER wid, dig;
   STRING text, number;
   DEFINE PadTo(n)=⊂
"            "[1 TO (n-LENGTH(text)-LENGTH(number))]⊃;

   IF
      unit = NULL_RECORD
    THEN BEGIN
      display(position,"[Input Signal]",pog);
      RETURN;
      END;

   GETFORMAT(wid,dig);

   text ← NULL;
   SETFORMAT(0,2);
   number ← CVF(get_spec(unit,"delay")*1000.0);
   text ← text&(PadTo(7)&number&"ms");
   SETFORMAT(0,3);
   number ← CVF(get_spec(unit,"gain"));
   text ← text&(PadTo(19)&number);
   SETFORMAT(0,2);
   number ← CVF(get_spec(unit,"decay"));
   text ← text&(PadTo(30)&number&"sec");
   SETFORMAT(0,0);
   number ← CVS(get_spec(unit,"#samples"));
   text ← text&(PadTo(45)&number);
   SETFORMAT(0,2);
   number ← CVF(get_spec(unit,"wall"));
   text ← text&(PadTo(59)&number&"m");
   SETFORMAT(0,2);
   number ← CVF(get_spec(unit,"frequency"));
   text ← text&(PadTo(71)&number&"Hz");
   SETFORMAT(0,0);
   number ← CVS(get_spec(unit,"rate"));
   text ← text&(PadTo(80)&number&"/sec");

   display(position,text,pog);
   SETFORMAT(wid,dig);
   END   "unit print";
PROCEDURE units_print(
      RECORD_POINTER(REV_UNIT) scratch, current;
      REAL delay_factor, gain_factor, duration_factor;
      INTEGER primes_offset;
      BOOLEAN nearest_prime_flag;
      REFERENCE BOOLEAN refresh_flag, factors_update_flag,
         scratch_update_flag;
      BOOLEAN current_update_flag);
   BEGIN "units print"

   IF
      refresh_flag
           ∨
      factors_update_flag
    THEN BEGIN
      label_print(UNIT_LABEL_POS,UNIT_LABEL_PIECE,
            delay_factor,gain_factor,duration_factor,
            primes_offset,nearest_prime_flag
            );
      factors_update_flag ← FALSE;
      END;

   IF
      refresh_flag
           ∨
      scratch_update_flag
    THEN BEGIN
      unit_print(UNIT_POS,UNIT_PIECE,scratch);
      scratch_update_flag ← FALSE;
      END;

   IF
      refresh_flag
           ∨
      current_update_flag
    THEN BEGIN
      unit_print(TREE_UNIT_POS,TREE_UNIT_PIECE,current);
      END;
   END   "units print";
PROCEDURE tree_print(
      RECORD_POINTER(REV_TREE) node);
   BEGIN "tree print"
   RECORD_POINTER(REV_TREE) tree;
   RECORD_POINTER(REV_UNIT) unit;
   INTEGER level, indent,
         wid, dig;
   BOOLEAN branching;
   STRING text;

   text ← NULL;

   GETFORMAT(wid,dig);
   SETFORMAT(0,2);

   IF
      node = NULL_RECORD
    THEN BEGIN
      display(TREE_POS,"NULL TREE"&↓,TREE_PIECE);
      RETURN;
      END;

   text ← text&("[Input Signal,");
   tree ← node;
   first_node(tree);
   IF
      move_tree(tree,unit,"OUTPUT")
    THEN BEGIN
      text ← text&(" Clock rate = "&CVS(get_spec(unit,"rate")));
      move_tree(tree,unit,"INPUT");
      END
    ELSE
      text ← text&(" Clock rate is undefined.");
   text ← text&("]");

   level ← 0; branching ← FALSE;
   WHILE
      traverse_tree(tree,unit,level,branching)
    DO BEGIN
      IF
         ¬branching
       THEN
         level ← 0;
      text ← text&(↓);
      FOR
         indent ← 1
       THRU
         level
       DO
         text ← text&("   ");
      text ← text&("[Delay:"&CVF(get_spec(unit,"delay")*1000.0)[2 TO ∞]&"ms");
      SETFORMAT(0,3);
      text ← text&(", Gain:"&CVF(get_spec(unit,"gain"))
			[(IF get_spec(unit,"gain") < 0 THEN 1 ELSE 2) TO ∞]);
      SETFORMAT(0,2);
      text ← text&(", Time:"&CVF(get_spec(unit,"decay"))[2 TO ∞]&"secs]");
      END;
   text ← text&(↓&" ");

   display(TREE_POS,text,TREE_PIECE);
   SETFORMAT(wid,dig);
   END   "tree print";
PROCEDURE update_display(
      RECORD_POINTER(REV_TREE) the_tree;
      RECORD_POINTER(REV_UNIT) the_unit;
      REAL the_delay_factor, the_gain_factor, the_duration_factor;
      INTEGER the_primes_offset;
      BOOLEAN the_nearest_prime_flag;
      REFERENCE INTEGER the_previous_position;
      REFERENCE BOOLEAN the_unit_update_flag,
            the_tree_update_flag,
            the_move_update_flag,
            the_factors_update_flag,
            the_refresh_flag);
   BEGIN "update display"
   RECORD_POINTER(REV_UNIT) the_tree_unit;
   RECORD_POINTER(REV_TREE) tree;
   RECORD_POINTER(REV_UNIT) unit;
   INTEGER line, level, indent;
   BOOLEAN branching;

   SAFE OWN INTEGER ARRAY PPINFO[0:24];

   ∂ Do a PPINFO uuo and check whether the display was manually cleared.;
   CODE('702240000000,PPINFO[0]);
   the_refresh_flag ← the_refresh_flag ∨
         (PPINFO[2] LAND '200000000000);

   IF
      the_refresh_flag
    THEN BEGIN
      PPSELECT(1); ∂ So interactions previous to REVED not clobbered.;
      TYPLOC(MAIN_POS-(PER_LINE*MAIN_LINES),MAIN_POS);
      PPSIZE(MAIN_LINES); ∂ This call is not superfluous! Sets lines/glitch = 1;
      esc_break("P",FALSE); ∂ Refresh page;
      END;
      
   the_move_update_flag ← the_move_update_flag ∨ the_tree_update_flag;
   move_tree(the_tree,the_tree_unit,"SELF");


   units_print(
      the_unit, the_tree_unit,
      the_delay_factor, the_gain_factor, the_duration_factor,
      the_primes_offset,
      the_nearest_prime_flag,
      the_refresh_flag, the_factors_update_flag,
         the_unit_update_flag, the_move_update_flag);

   IF
      the_tree_update_flag
              ∨
      the_refresh_flag
    THEN BEGIN
      tree_print(the_tree);
      the_tree_update_flag ← FALSE;
      END;

   ∂  ... update_display ... continued.;

   IF
      the_move_update_flag
              ∨
      the_refresh_flag
    THEN BEGIN
      tree ← the_tree;
      first_node(tree);

      line ← TREE_POS-(PER_LINE*the_previous_position);
      cursor(line," ",CURSOR_PIECE);

      the_previous_position ← 0;
      WHILE
         tree ≠ the_tree
                ∧
         traverse_tree(tree,unit,level,branching)
       DO
         the_previous_position ←
               the_previous_position+1;

      line ← TREE_POS-(PER_LINE*the_previous_position);
      cursor(line,"→",CURSOR_PIECE);

      the_move_update_flag ← FALSE;
      END;

   the_refresh_flag ← FALSE;
   END   "update display";
∂ Declarations for THE_WORLD.;

STRING the_in_device, the_in_file;
INTEGER the_in_channel, the_in_eof, the_in_break;
BOOLEAN the_file_flag;

STRING the_command, the_arguments;

RECORD_POINTER(REV_TREE) the_tree;
RECORD_POINTER(REV_UNIT) the_unit, the_tree_unit;
	∂ the_tree_unit is non-essential;

BOOLEAN the_nearest_prime_flag;

INTEGER the_primes_offset;
REAL the_duration_factor,
      the_delay_factor,
      the_gain_factor;

INTEGER the_id,
      the_previous_position;

BOOLEAN the_unit_update_flag,
      the_tree_update_flag,
      the_move_update_flag,
      the_factors_update_flag,
      the_refresh_flag;

BOOLEAN the_describe_flag, the_describing_flag;

PROCEDURE initialize_the_world;
   BEGIN "initialize the world"
   ∂ BEWARE BEWARE BEWARE ... Uses all GLOBAL variables.;

   DCLEAR(IMPULSE_PIECE);

   the_command ← the_arguments ← NULL;
   the_describe_flag ← the_describing_flag ← FALSE;

   free_tree(the_tree);
   free_node(the_tree);
   the_tree ← new_tree;
   free_unit(the_tree_unit);

   free_unit(the_unit);
   the_unit ← new_unit(DEFAULT_CLOCK_RATE,DEFAULT_GAIN,DEFAULT_DELAY);

   the_nearest_prime_flag ← DEFAULT_NEAREST_PRIME_FLAG;

   the_primes_offset ← DEFAULT_PRIMES_OFFSET;
   the_duration_factor ← DEFAULT_DURATION_FACTOR;
   the_delay_factor ← DEFAULT_DELAY_FACTOR;
   the_gain_factor ← DEFAULT_GAIN_FACTOR;

   the_unit_update_flag ← TRUE;
   the_tree_update_flag ← TRUE;
   the_move_update_flag ← TRUE;
   the_factors_update_flag ← TRUE;
   the_refresh_flag ← TRUE;

   display(HELP_POS,HELP_TEXT,HELP_PIECE);

   END   "initialize the world";
∂ Top level.;

EXTERNAL INTEGER RPGSW;

the_in_device ← the_in_file ← NULL;
the_in_channel ← the_in_eof ← the_in_break ← 0;
the_file_flag ← FALSE;
IF
   RPGSW
 THEN BEGIN
   the_in_device ← "DSK";
   the_in_file ← DEFAULT_SAVE_FILE;
   the_in_channel ←
         file_channel(the_in_device,the_in_file,"in",'00,
               the_in_eof,the_in_break);
   IF
      the_in_channel ≥ 0
    THEN
      the_file_flag ← TRUE;
   END;

the_id ← 0;

∂ display(HELP_POS,HELP_TEXT,HELP_PIECE);

PPSELECT(1); ∂ So interactions previous to REVED not clobbered.;
TYPLOC(MAIN_POS-(PER_LINE*MAIN_LINES),MAIN_POS);
PPSIZE(MAIN_LINES); ∂ This call is not superfluous! Sets lines/glitch = 1;

the_previous_position ← 0;
initialize_the_world;

WHILE
   TRUE
 DO BEGIN "command loop"
   LABEL Unknown_command;

   the_describe_flag ←
         the_describing_flag ←
               the_describe_flag ∧ ¬the_describing_flag;
   IF
      ¬the_describing_flag
    THEN BEGIN
      TTYUP(TRUE);

      IF
	 ¬the_file_flag
       THEN BEGIN
	 update_display(
	       the_tree,
	       the_unit,
               the_delay_factor,
               the_gain_factor,
               the_duration_factor,
               the_primes_offset,
               the_nearest_prime_flag,
	       the_previous_position,
	       the_unit_update_flag,
	       the_tree_update_flag,
	       the_move_update_flag,
	       the_factors_update_flag,
	       the_refresh_flag);
	 PRINT("?: ");
	 END;

      command_read(the_command,the_arguments,
	    the_file_flag,
	    the_in_channel,
	    the_in_eof, the_in_break);
      END;
   CASE
      the_command LAND '177
    OF BEGIN "command select"

       DEFINE DESCRIPTION(text)=⊂
	 IF
	    the_describe_flag
	  THEN BEGIN
	    PRINT(↓,
                  text,
                  ↓
		  );
	    the_describe_flag ← FALSE;
	    END
	  ELSE ⊃;

       DEFINE PREFIX_EQU(s1,s2)=
         ⊂EQU(s1,s2[1 FOR LENGTH(s1)])⊃;
         ∂ If s2="FOO" is TRUE for s1=NULL,"F","FO","FOO", but not
            "X","FX","FOX","FOOX",etc. ;
       DEFINE CHECK_COMMAND(a_command)=⊂
         IF
            ¬ PREFIX_EQU(the_command,a_command)
          THEN
            GO TO Unknown_command	∂ Lord have mercy! A GO TO!!;
          ELSE ⊃;

      ["A"]   ∂ APPEND;
         CHECK_COMMAND("APPEND")
         DESCRIPTION(
<"APPEND
Inserts a copy of the scratch unit into the tree following the current
unit and preceding all of the outputs of the current unit.  The inserted
unit will be the new current unit.">)
         BEGIN
         insert_node(the_tree,the_unit,"OUTPUT");
         the_tree_update_flag ← TRUE;
         END;
      ["B"]   ∂ BRANCH;
         CHECK_COMMAND("BRANCH")
         DESCRIPTION(
<"BRANCH
Inserts a copy of the scratch unit into the tree in parallel with the
current unit, so that the inserted unit shares the same input source
with the current unit.  The inserted unit will be the new current unit.">)
         BEGIN
         insert_node(the_tree,the_unit,"BRANCH");
         the_tree_update_flag ← TRUE;
         END;
      ["C"]   ∂ CLOCK_RATE;
         CHECK_COMMAND("CLOCK_RATE")
         DESCRIPTION(
<"CLOCK_RATE {value{K}} {fix}
Sets the clock rate for the scratch unit to the given value.  If the
letter ""K"" follows the value, then it is multiplied by 1000.0.  If
the value is less than 1000.0 (as in 256), then it is multiplied by 100.0.
The fix option is the same as in the DELAY command.">)
         BEGIN
         REAL value, realtmp;
         STRING fix;
         real_fetch(the_arguments,
               value ← get_spec(the_unit,"rate"),
               realtmp ← 0);
         IF
            value < 1000.0
          THEN
            value ← value*100;
         fix_fetch(the_arguments,fix ← "decay");
         set_spec(the_unit,"rate",fix,value,the_nearest_prime_flag);
         the_unit_update_flag ← TRUE;
         END;
      ["D"]   ∂ DELAY_TIME;
         CHECK_COMMAND("DELAY_TIME")
         DESCRIPTION(
<"DELAY_TIME {value | [*|/]{factor}} {fix}
Sets the delay time in milliseconds for the scratch unit to either the
specified value or else the specified factor times (or divided into) the
present value.  If ""*"" or ""/"" is typed without a factor, the last one given
is used.  Fix (default GAIN) is the name of the parameter to be held constant.">)
         BEGIN
         REAL value;
         STRING fix;
         real_fetch(the_arguments,
               value ← get_spec(the_unit,"delay")*1000.0,
               the_delay_factor);
         IF
            the_delay_factor = 0.0
          THEN
            the_delay_factor ← DEFAULT_DELAY_FACTOR;
         IF
            value = 0.0
          THEN
            value ← DEFAULT_DELAY*1000.0;
         fix_fetch(the_arguments,fix ← "gain");
         set_spec(the_unit,"delay",fix,value*0.001,the_nearest_prime_flag);
         the_unit_update_flag ← TRUE;
         the_factors_update_flag ← TRUE;
         END;
      ["E"]   ∂ EXIT;
         CHECK_COMMAND("EXIT")
         DESCRIPTION(
<"EXIT {file}
Returns to the monitor after saving the state of the editor in the
specified file.  If no name is given the state is not saved.  Unspecified
fields of the specified file are filled in from the name DSK:",DEFAULT_SAVE_FILE,".
The ""@"" command will read in saved states.">)
         BEGIN
         ∂ Use same default file as @, SAVE_THE_WORLD;
         STRING device, file;
         INTEGER channel, eof, break;
         IF
            file_fetch(the_arguments,device ← "DSK",file ← DEFAULT_SAVE_FILE)
          THEN BEGIN
	    channel ← file_channel(device,file,"out",'00,eof,break);
	    IF
	       channel ≥ 0
	     THEN BEGIN
	       save_the_world(channel,
		     the_tree,
		     the_unit,
		     the_nearest_prime_flag,
		     the_primes_offset,
		     the_duration_factor,
		     the_delay_factor,
		     the_gain_factor);
	       RELEASE(channel);
	       DONE "command loop";
	       END
	     ELSE
	       PRINT(↓,"Can't write file ",device,":",file,↓);
	    END
          ELSE
	    DONE "command loop";
         END;
      ["F"]   ∂ FREQUENCY;
         CHECK_COMMAND("FREQUENCY")
         DESCRIPTION(
<"FREQUENCY {value} {fix}
Sets the delay time of the scratch unit to the reciprocal of the
specified value.  Fix is the same as for the DELAY_TIME command.">)
         BEGIN
         REAL value, realtmp;
         STRING fix;
         real_fetch(the_arguments,
               value ← get_spec(the_unit,"frequency"),
               realtmp ← 0);
         fix_fetch(the_arguments,fix ← "gain");
         set_spec(the_unit,"frequency",fix,value,the_nearest_prime_flag);
         the_unit_update_flag ← TRUE;
         END;
      ["G"]   ∂ GAIN;
         CHECK_COMMAND("GAIN")
         DESCRIPTION(
<"GAIN {value | [*|/]{factor}} {fix}
Sets the gain of the scratch unit to either the specified value or the
given factor times (or divided into) the present value.  If ""*"" or ""/""
is typed without a factor, the last one given is used.  Fix is the name
of the parameter to be held constant.  The default is DELAY.">)
         BEGIN
         REAL value;
         STRING fix;
         real_fetch(the_arguments,
               value ← get_spec(the_unit,"gain"),
               the_gain_factor);
         IF
            the_gain_factor = 0.0
          THEN
            the_gain_factor ← DEFAULT_GAIN_FACTOR;
         IF
            value = 0.0
          THEN
            value ← DEFAULT_GAIN;
         fix_fetch(the_arguments,fix ← "delay");
         set_spec(the_unit,"gain",fix,value,the_nearest_prime_flag);
         the_unit_update_flag ← TRUE;
         the_factors_update_flag ← TRUE;
         END;
      ["H"]   ∂ HELP;
         CHECK_COMMAND("HELP")
         DESCRIPTION(
<"HELP
Displays a list of all available command names.  For information
about specific commands, type ""?"" followed by the name of the
command.  For general information, read REVED.KS[DOC,MUS].">)
         BEGIN
         DCLEAR(IMPULSE_PIECE);
         display(HELP_POS,HELP_TEXT,HELP_PIECE);
         the_refresh_flag ← TRUE;
         END;
      ["I"]   ∂ IMPULSE;
         CHECK_COMMAND("IMPULSE")
         DESCRIPTION(
<"IMPULSE {duration | [*|/]{factor}} {file}
Displays the impulse response of the path from the Input Signal thru the
current unit for the specified duration, or if none is given, then for
the longest decay time on that path.  A factor applies to the default time,
otherwise behaving as in DELAY_TIME.  A file name saves the display.">)
         BEGIN
         ∂ May also allow path_length in future.;
         REAL duration;
         STRING device, file;
         BOOLEAN save;
         INTEGER channel, eof, break;
         RECORD_POINTER(CASCADE) chain;
         RECORD_POINTER(REV_TREE) tmp_tree;
         RECORD_POINTER(REV_UNIT) tmp_unit;

         tmp_tree ← the_tree;
         IF
            move_tree(tmp_tree,tmp_unit,"SELF")
                     ∧
            ¬is_root(tmp_tree)
          THEN
            duration ← get_spec(tmp_unit,"decay")
          ELSE
            duration ← 1.0;
         WHILE
            move_tree(tmp_tree,tmp_unit,"INPUT")
                     ∧
            ¬is_root(tmp_tree)
          DO
            duration ← duration MAX get_spec(tmp_unit,"decay");
         real_fetch(the_arguments,
               duration,
               the_duration_factor);
         IF
            the_duration_factor = 0.0
          THEN
            the_duration_factor ← DEFAULT_DURATION_FACTOR;
         save ← file_fetch(the_arguments,
                  device ← "DSK",
                  file ← "IMPLSE.PLT");
         chain ← new_cascade(the_tree);
         IMPULS(the_id,chain,duration);

         IF
            ¬(chain = NULL_RECORD
                  ∧
            the_file_flag)
          THEN BEGIN
            HELP_clear;
            WRITE(the_id,IMPULSE_PIECE);
            END;

         IF
            save
          THEN BEGIN
            channel ← file_channel(device,file,"out",'17,eof,break);
            IF
               channel ≥ 0
             THEN BEGIN
               DWRITE(the_id,channel);
               RELEASE(channel);
               END
             ELSE
               PRINT(↓,"Can't write file ",device,":",file,↓);
            END;
         free_cascade(chain);
         the_factors_update_flag ← TRUE;
         the_refresh_flag ← TRUE;
         END;
      ["K"]   ∂ KILL;
         CHECK_COMMAND("KILL")
         DESCRIPTION(
<"KILL {YES}
Removes the current unit from the tree and throws it away.  If you don't
say YES in the command, it will ask if you really mean it (since the unit
cannot be recovered).  Any output units of the current unit will then get
input from the input to the current unit.  A new current unit is chosen.">)
         BEGIN
         STRING reply;
         BOOLEAN do_it;
         IF
            ¬yes_fetch(the_arguments,do_it ← FALSE)
          THEN BEGIN
            PRINT("REALLY KILL? ");
            TTYUP(TRUE);
            reply ← INCHWL;
            yes_fetch(reply,do_it ← FALSE);
            END;
         IF
            do_it
          THEN
            delete_node(the_tree);
         the_tree_update_flag ← TRUE;
         END;
      ["L"]   ∂ LAST_IMPULSE;
         CHECK_COMMAND("LAST_IMPULSE")
         DESCRIPTION(
<"LAST_IMPULSE {file}
The last impulse response displayed with the IMPULSE command may be
redisplayed and optionally saved in a file.  The default file will only
be used to fill in unspecified fields of a named file; it is IMPLSE.PLT.
To list on the XGP, try ""R XIP;X1.4;Y1.4;H2;⊗file;"".">)
         BEGIN
         STRING device, file;
         INTEGER channel, eof, break;
         BOOLEAN save;
         IF
            the_id ≠ 0
          THEN BEGIN
            save ← file_fetch(the_arguments,
                     device ← "DSK",
                     file ← "IMPLSE.PLT");
            HELP_clear;
            WRITE(the_id,IMPULSE_PIECE);
            IF
               save
             THEN BEGIN
               channel ← file_channel(device,file,"out",'17,eof,break);
               IF
                  channel ≥ 0
                THEN BEGIN
                  DWRITE(the_id,channel);
                  RELEASE(channel);
                  END
                ELSE
                  PRINT(↓,"Can't write file ",device,":",file,↓);
               END;
            END;
         the_refresh_flag ← TRUE;
         END;
      ["N"]   ∂ NEAREST_PRIMES;
         CHECK_COMMAND("NEAREST_PRIMES")
         DESCRIPTION(
<"NEAREST_PRIMES {YES | NO}
Sets the flag which determines whether the number of samples of delay
for the scratch unit will be coerced to be prime numbers (a good idea).
If no argument is given, the flag is set to the opposite of its current
state.  The initial value of this flag is YES.">)
         BEGIN
         yes_fetch(the_arguments,
                  the_nearest_prime_flag ← ¬the_nearest_prime_flag);
         the_factors_update_flag ← TRUE;
         END;
      ["O"]   ∂ OUTPUT;
         CHECK_COMMAND("OUTPUT")
         DESCRIPTION(
<"OUTPUT {file}
Writes out the tree as an instrument suitable for compilation by the
music compiler.  The default file name is ALLPAS.MUS.">)
         BEGIN
         STRING device, file;
         INTEGER channel, eof, break;
         file_fetch(the_arguments,device ← "DSK",file ← "ALLPAS.MUS");
         channel ← file_channel(device,file,"out",'00,eof,break);
         IF
            channel ≥ 0
          THEN BEGIN
            instrument_print(channel,the_tree);
            RELEASE(channel);
            END
          ELSE
            PRINT(↓,"Can't write file ",device,":",file,↓);
         END;
      ["P"]   ∂ PRECEDE;
         CHECK_COMMAND("PRECEDE")
         DESCRIPTION(
<"PRECEDE
Inserts a copy of the scratch unit into the tree preceding the current
unit, so that the current unit will then get its input from the new
unit.  The inserted unit becomes the current unit.">)
         BEGIN
         insert_node(the_tree,the_unit,"INPUT");
         the_tree_update_flag ← TRUE;
         END;
      ["R"]   ∂ REPLACE;
         CHECK_COMMAND("REPLACE")
         DESCRIPTION(
<"REPLACE
The parameters of the current unit are replaced by the parameters of the
scratch unit.  To copy parameters the from the current unit to the
scratch unit, see the "" (read as ditto) command.">)
         BEGIN
         insert_node(the_tree,the_unit,"SELF",TRUE);
         the_tree_update_flag ← TRUE;
         END;
      ["S"]   ∂ SAVE_THE_WORLD;
         CHECK_COMMAND("SAVE_THE_WORLD")
         DESCRIPTION(
<"SAVE_THE_WORLD {file}
The entire state of the editor is saved as a sequence of commands in the
specified file.  The default file used is ",DEFAULT_SAVE_FILE,".  The ""@""
command, which uses the same default file, will read in a saved file.">)
         BEGIN
         ∂ Use same default file as @ ;
         STRING device, file;
         INTEGER channel, eof, break;
         file_fetch(the_arguments,device ← "DSK",file ← DEFAULT_SAVE_FILE);
         channel ← file_channel(device,file,"out",'00,eof,break);
         IF
            channel ≥ 0
          THEN BEGIN
            save_the_world(channel,
                  the_tree,
                  the_unit,
                  the_nearest_prime_flag,
                  the_primes_offset,
                  the_duration_factor,
                  the_delay_factor,
                  the_gain_factor);
            RELEASE(channel);
            END
          ELSE
            PRINT(↓,"Can't write file ",device,":",file,↓);
         END;
      ["T"]   ∂ TIME_TO_DECAY;
         CHECK_COMMAND("TIME_TO_DECAY")
         DESCRIPTION(
<"TIME_TO_DECAY {value} {fix}
Sets the time to decay parameter for the scratch unit.  This is the time
required for an input signal to decay 60dB.  Fix is the name of the
parameter to be held constant.  The default is DELAY.">)
         BEGIN
         REAL value, realtmp;
         STRING fix;
         real_fetch(the_arguments,
               value ← get_spec(the_unit,"decay"),
               realtmp ← 0);
         fix_fetch(the_arguments,fix ← "delay");
         set_spec(the_unit,"decay",fix,value,the_nearest_prime_flag);
         the_unit_update_flag ← TRUE;
         END;
      ["W"]   ∂ WALL_DISTANCE;
         CHECK_COMMAND("WALL_DISTANCE")
         DESCRIPTION(
<"WALL_DISTANCE {value} {fix}
Sets the delay time for the scratch unit so that it corresponds to the
given distance (in meters) from a reflective surface.  Fix is the same
as for the DELAY_TIME command.">)
         BEGIN
         REAL value, realtmp;
         STRING fix;
         real_fetch(the_arguments,
               value ← get_spec(the_unit,"wall"),
               realtmp ← 0);
         fix_fetch(the_arguments,fix ← "gain");
         set_spec(the_unit,"wall",fix,value,the_nearest_prime_flag);
         the_unit_update_flag ← TRUE;
         END;
      ["Z"]   ∂ ZERO_WORLD;
         CHECK_COMMAND("ZERO_WORLD")
         DESCRIPTION(
<"ZERO_WORLD
Re-initializes the state of the editor, throwing away anything done so
far.  This is a useful command with which to begin command files.">)
         BEGIN
         initialize_the_world;
         END;
      ["#"]   ∂ #SAMPLES;
         CHECK_COMMAND("#SAMPLES")
         DESCRIPTION(
<"#SAMPLES {value | [+|-]{offset}} {fix}
Sets the number of samples of delay for the scratch unit to the given
value.  If an offset is given instead, it is taken as the number of
prime numbers to offset from the current value.  Fix is the name of the
parameter to hold constant.  The default is DECAY.">)
         BEGIN
         INTEGER value;
         STRING fix;
         #samp_fetch(the_arguments,
               value ← get_spec(the_unit,"#samples"),
               the_primes_offset);
         fix_fetch(the_arguments,fix ← "decay");
         set_spec(the_unit,"#samples",fix,value,the_nearest_prime_flag);
         the_unit_update_flag ← TRUE;
         the_factors_update_flag ← TRUE;
         END;
      [""""]  ∂ COPY_NODE_INTO_UNIT;
         CHECK_COMMAND("""")
         DESCRIPTION(
<"""
Copy the parameters of the current unit into the scratch unit.  This is
the inverse of the REPLACE command, and is how you edit units already in
the tree.">)
         BEGIN
         move_tree(the_tree,the_unit,"SELF");
         the_unit_update_flag ← TRUE;
         END;
      ["."]   ∂ REFRESH_DISPLAY;
         CHECK_COMMAND(".")
         DESCRIPTION(
<".
Refreshes the display if it has been cleared by an ESC or BREAK keyboard
command or otherwise garbaged.">)
         BEGIN
         the_refresh_flag ← TRUE;
         END;
      ["@"]   ∂ READ_COMMANDS_FROM_FILE;
         DESCRIPTION(
<"@ {file}
Take all further commands from the specified file until either an
end-of-file or a "";"" command.  This command will restore the state
of the editor from the files created by the SAVE_THE_WORLD and EXIT
commands.  The default file is ",DEFAULT_SAVE_FILE,".">)
         BEGIN
         ∂ Use same default file as SAVE_WORLD.;
         IF
            the_file_flag
          THEN
            PRINT(↓,"No @ nesting.",↓)
          ELSE BEGIN
            file_fetch(the_arguments,
                  the_in_device ← "DSK",the_in_file ← DEFAULT_SAVE_FILE);
            the_in_channel ←
                  file_channel(the_in_device,the_in_file,"in",'00,
                        the_in_eof,the_in_break);
            IF
               the_in_channel < 0
             THEN
               PRINT(↓,"Can't read file ",the_in_device,":",the_in_file,↓)
             ELSE
               the_file_flag ← TRUE;
            END;
         END;
      [";"]   ∂ READ_COMMANDS_FROM_TTY;
         DESCRIPTION(
<";
When read from a file of commands, this is equivalent to an end-of-file;
otherwise it has no effect.">)
         BEGIN
         IF
            the_file_flag
          THEN BEGIN
            RELEASE(the_in_channel);
            PRINT(↓);
            END;
         the_file_flag ← FALSE;
         END;
      ["?"]   ∂ HELP prefix;
         DESCRIPTION(
<"? {command}
Types out a brief description of the command named.  The format of the
first line will be: ""{}"" surround options, ""[]"" surround required
arguments, ""|"" separates alternatives, ""lower_case"" indicates a class
of possible arguments (using a suggestive name).">)
         BEGIN
         ∂ Describe command;
	 INTEGER break;
	 the_command ← SCAN(the_arguments,CMMANDBREAKS,break);
	 IF
	    LENGTH(the_command) = 0
	  THEN
            IF
               LENGTH(the_arguments) > 0
             THEN
               the_command ← LOP(the_arguments);
	 IF
	    LENGTH(the_command) = 0
	  THEN
	    PRINT(↓,
"An introduction to this program may be found in REVED.KS[DOC,MUS].  For
information about specific commands type ""?"" followed by the name of
the command.  The command HELP displays a list of all the commands.  All
commands may be abbreviated by their first letter, and only enough
blanks to insure decipherability need be typed.",
                  ↓
		  )
	  ELSE
	    the_describe_flag ← TRUE;
	 END;
      ["←"]   ∂ MOVE_TO_INPUT;
         DESCRIPTION(
<"←	(no carriage return required)
Makes the input unit of the current unit the new current unit.  Normally
requires no carriage return; however, if another command was typed first
and then backspaced over, a carriage return may be needed.">)
         BEGIN
         the_move_update_flag ←
               move_tree(the_tree,the_tree_unit,"INPUT")
                         ∨
               the_move_update_flag;
         END;
      ["→"]   ∂ MOVE_TO_OUTPUT;
         DESCRIPTION(
<"←	(no carriage return required)
Makes the first listed output unit of the current unit the new current
unit.  Normally requires no carriage return; however, if another command
was typed first and then backspaced over, a carriage return may be needed.">)
         BEGIN
         the_move_update_flag ←
               move_tree(the_tree,the_tree_unit,"OUTPUT")
                         ∨
               the_move_update_flag;
         END;
      ["↔"]   ∂ MOVE_TO_BRANCH;
         DESCRIPTION(
<"↔	(no carriage return required)
Makes the next branch unit receiving input from the same unit as the
current unit the new current unit.  Normally requires no carriage return;
however, if another command was typed first and then backspaced over, a
carriage return may be needed.">)
         BEGIN
         the_move_update_flag ←
               move_tree(the_tree,the_tree_unit,"BRANCH")
                         ∨
               the_move_update_flag;
         END;

      [0]     ∂ NULL COMMAND;
         ;
      ELSE    ∂ UNRECOGNIZED COMMAND;
   Unknown_command:
         DESCRIPTION(
<"Unknown command.  Type ? for help.">)
         BEGIN
         PRINT(" Unknown command.  Type ? for help.",↓);
         END

      END   "command select";
   END "command loop";

PPSELECT(0);
esc_break("N",TRUE); ∂ Clear and normalize page;
END   "FACADE"